import json
import logging
import os
import time
import traceback

import requests
from codes.models import Code
from django.conf import settings
from django.utils import timezone

from .code_executor import CodeExecutor
from .models import Battle, BattleRound, BattleStatistics
from .node_server_manager import get_node_server_manager
from .monitoring import send_battle_alert, AlertType, AlertLevel

logger = logging.getLogger(__name__)


# 通用的重试保存函数
def retry_save_operation(save_func, max_retries=3, debug_logger=None, operation_name='保存操作'):
    """通用的重试保存函数，处理SQLite锁定问题和UNIQUE constraint错误"""
    for attempt in range(max_retries):
        try:
            if debug_logger:
                debug_logger.info(f'尝试{operation_name} (第{attempt + 1}次)')

            save_func()
            if debug_logger:
                debug_logger.info(f'{operation_name}完成')
            break
        except Exception as save_error:
            error_str = str(save_error).lower()

            # 检查是否为UNIQUE constraint错误
            if 'unique constraint failed' in error_str and 'battles_battleround' in error_str:
                if debug_logger:
                    debug_logger.warning(f'检测到回合数据重复保存错误，跳过此次保存: {save_error}')
                    debug_logger.warning('这通常是由于回合号同步问题导致的，已通过修复避免')
                # 对于UNIQUE constraint错误，直接跳过不重试
                break

            if debug_logger:
                # 输出详细的错误信息
                debug_logger.error(f'{operation_name}失败 (第{attempt + 1}次)')
                debug_logger.error(f'错误类型: {type(save_error).__name__}')
                debug_logger.error(f'错误信息: {str(save_error)}')

                # 详细分析错误类型
                error_analysis = _analyze_database_error(save_error)
                debug_logger.error(f'错误分析: {error_analysis}')

            if attempt == max_retries - 1:
                if debug_logger:
                    debug_logger.error(f'{operation_name}最终失败，完整异常堆栈:')
                    debug_logger.error(traceback.format_exc())
                    debug_logger.error('=== 数据库保存操作彻底失败 ===')
                    debug_logger.error(f'=== 错误总结: {_analyze_database_error(save_error)} ===')
                raise
            else:
                # 等待一小段时间后重试
                time.sleep(0.1 * (attempt + 1))
                if debug_logger:
                    debug_logger.info(f'等待{0.1 * (attempt + 1)}秒后重试...')


def _analyze_database_error(error):
    """分析数据库错误类型并返回详细描述"""
    error_str = str(error).lower()
    error_type = type(error).__name__

    # Django数据库错误分析
    from django.db import DataError, IntegrityError, OperationalError

    if isinstance(error, IntegrityError):
        if 'unique' in error_str:
            return '数据完整性错误 - 唯一性约束违反（重复数据）'
        elif 'foreign key' in error_str:
            return '数据完整性错误 - 外键约束违反（关联数据不存在）'
        elif 'not null' in error_str:
            return '数据完整性错误 - 非空约束违反（必填字段为空）'
        else:
            return '数据完整性错误 - 约束违反'

    elif isinstance(error, DataError):
        if 'value too long' in error_str:
            return '数据格式错误 - 字段值长度超限'
        elif 'invalid' in error_str and 'type' in error_str:
            return '数据格式错误 - 数据类型不匹配'
        elif 'datetime' in error_str or 'date' in error_str:
            return '数据格式错误 - 日期时间格式不正确'
        else:
            return '数据格式错误 - 数据格式不符合要求'

    elif isinstance(error, OperationalError):
        if 'database is locked' in error_str:
            return '操作错误 - 数据库被锁定（可能是并发访问冲突）'
        elif 'no such table' in error_str:
            return '操作错误 - 数据表不存在（数据库结构问题）'
        elif 'no such column' in error_str:
            return '操作错误 - 字段不存在（数据库结构不匹配）'
        elif 'timeout' in error_str:
            return '操作错误 - 数据库操作超时'
        else:
            return '操作错误 - 数据库操作失败'

    # SQLite特定错误分析
    import sqlite3

    if isinstance(error, sqlite3.Error):
        if 'database is locked' in error_str:
            return 'SQLite锁定错误 - 数据库文件被其他进程锁定'
        elif 'constraint' in error_str:
            return 'SQLite约束错误 - 数据约束违反'
        elif 'datatype' in error_str:
            return 'SQLite数据类型错误 - 数据类型不匹配'
        else:
            return f'SQLite错误 - {error_str}'

    # 通用错误分析
    if 'field' in error_str or 'column' in error_str:
        return '字段错误 - 字段名称或类型不匹配'
    elif 'connection' in error_str:
        return '连接错误 - 数据库连接问题'
    elif 'permission' in error_str:
        return '权限错误 - 数据库访问权限不足'
    elif 'syntax' in error_str:
        return '语法错误 - SQL语句语法错误'
    else:
        return f'未知错误类型 - {error_type}: {error_str[:100]}'


# 创建专门的调试日志记录器
def create_debug_logger(battle_id: str):
    """为特定战斗创建分级调试日志记录器"""
    logger_name = f'battle_debug_{battle_id}'
    debug_logger = logging.getLogger(logger_name)

    # 如果logger已经配置过，直接返回
    if debug_logger.handlers:
        return debug_logger

    debug_logger.setLevel(logging.DEBUG)

    # 创建文件处理器 - 保存到项目logs目录
    logs_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'logs')
    os.makedirs(logs_dir, exist_ok=True)

    # 创建详细格式器，包含日志级别和更多信息
    formatter = logging.Formatter('%(asctime)s - %(levelname)-8s - [%(name)s] - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')

    # 1. 按战斗ID分离的日志文件（保留原有功能）
    log_file = os.path.join(logs_dir, f'battle_debug_{battle_id}.log')
    file_handler = logging.FileHandler(log_file, mode='w', encoding='utf-8')
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(formatter)
    debug_logger.addHandler(file_handler)

    # 2. 通用的battle_debug.log文件（新覆盖形式）
    general_log_file = os.path.join(logs_dir, 'battle_debug.log')
    general_file_handler = logging.FileHandler(general_log_file, mode='w', encoding='utf-8')
    general_file_handler.setLevel(logging.DEBUG)
    general_file_handler.setFormatter(formatter)
    debug_logger.addHandler(general_file_handler)

    # 防止日志传播到父logger
    debug_logger.propagate = False

    # 添加便捷的分级日志方法
    def log_round_start(round_num):
        debug_logger.info(f'{"=" * 60}')
        debug_logger.info(f'🚀 开始回合 {round_num}')
        debug_logger.info(f'{"=" * 60}')

    def log_round_end(round_num, success=True, details=''):
        status = '✅ 成功' if success else '❌ 失败'
        debug_logger.info(f'{"=" * 60}')
        debug_logger.info(f'🏁 回合 {round_num} 结束 - {status}' + (f' - {details}' if details else ''))
        debug_logger.info(f'{"=" * 60}')

    def log_player_action(player_name, action, details=''):
        debug_logger.info(f'👤 玩家 {player_name} - {action}' + (f' - {details}' if details else ''))

    def log_server_communication(direction, endpoint, data_summary='', level='DEBUG'):
        arrow = '➡️' if direction == 'send' else '⬅️'
        msg = f'{arrow} {direction.upper()} {endpoint}' + (f' - {data_summary}' if data_summary else '')
        if level.upper() == 'INFO':
            debug_logger.info(msg)
        else:
            debug_logger.debug(msg)

    def log_error_with_context(error_msg, context='', exc_info=None):
        full_msg = f'❌ 错误: {error_msg}' + (f' - 上下文: {context}' if context else '')
        debug_logger.error(full_msg, exc_info=exc_info)

    def log_warning_with_context(warning_msg, context=''):
        debug_logger.warning(f'⚠️ 警告: {warning_msg}' + (f' - 上下文: {context}' if context else ''))

    def log_performance(operation, duration, details=''):
        debug_logger.info(f'⏱️ 性能: {operation} 耗时 {duration:.2f}秒' + (f' - {details}' if details else ''))

    def log_sync_info(sync_type, old_value, new_value):
        debug_logger.info(f'🔄 同步: {sync_type} {old_value} -> {new_value}')

    # 将便捷方法绑定到logger对象
    debug_logger.log_round_start = log_round_start
    debug_logger.log_round_end = log_round_end
    debug_logger.log_player_action = log_player_action
    debug_logger.log_server_communication = log_server_communication
    debug_logger.log_error_with_context = log_error_with_context
    debug_logger.log_warning_with_context = log_warning_with_context
    debug_logger.log_performance = log_performance
    debug_logger.log_sync_info = log_sync_info

    return debug_logger


class BattleEngine:
    """战斗执行引擎 - 整合脚本执行、命令处理和状态同步"""

    def __init__(self, battle_id: str):
        self.battle_id = battle_id
        self.battle = None
        self.code_executor = CodeExecutor()
        self.node_server = None
        self.is_running = False
        self.last_known_round = 0  # 记录最后已知的回合数，用于战斗结束后避免访问Node.js服务器
        self.battle_finished = False  # 标记战斗是否已结束
        self.round_limit = settings.BATTLE_ENGINE.get('MAX_ROUNDS', 200)  # 默认最大回合数
        self.round_timeout = settings.BATTLE_ENGINE.get('ROUND_TIMEOUT', 30)  # 每回合超时时间（秒）
        self.debug_logger = create_debug_logger(battle_id)  # 创建调试日志记录器
        self.verbose_battle_state = settings.BATTLE_ENGINE.get('VERBOSE_BATTLE_STATE', False)  # 调试开关：是否打印完整battleState信息
        self._load_battle_config()

    def _load_battle_config(self):
        """从battle对象中加载配置"""
        try:
            # 获取battle对象
            battle = Battle.objects.get(id=self.battle_id)

            # 从game_config中读取round_limit
            if battle.game_config:
                game_config = json.loads(battle.game_config) if isinstance(battle.game_config, str) else battle.game_config
                round_limit = game_config.get('round_limit', self.round_limit)
                self.round_limit = round_limit
                self.debug_logger.info(f'从game_config中读取到round_limit: {round_limit}')
            else:
                self.debug_logger.info(f'game_config为空，使用默认round_limit: {self.round_limit}')

        except Exception as e:
            self.debug_logger.warning(f'加载battle配置失败: {e}，使用默认round_limit: {self.round_limit}')

    def set_verbose_battle_state(self, enabled: bool):
        """设置是否打印详细的battleState信息"""
        self.verbose_battle_state = enabled
        self.debug_logger.info(f'battleState详细日志已{"开启" if enabled else "关闭"}')

    def initialize(self):
        """初始化战斗引擎"""
        try:
            self.debug_logger.info(f'开始初始化战斗 {self.battle_id}')

            # 获取战斗记录（包含关联对象和版本信息）
            self.battle = (
                Battle.objects.select_related('player1', 'player2', 'player1_code', 'player2_code')
                .prefetch_related('player1_code__versions', 'player2_code__versions')
                .get(id=self.battle_id)
            )
            player2_name = self.battle.player2.username if self.battle.player2 else 'AI'
            self.debug_logger.info(f'获取战斗信息成功: 玩家1={self.battle.player1.username}, 玩家2={player2_name}')

            # 启动Node.js战斗服务器
            manager = get_node_server_manager()

            # 先清理不健康的服务器
            manager.cleanup_unhealthy_servers()

            self.node_server = manager.create_battle_server(self.battle_id, self._get_battle_config())
            if not self.node_server:
                raise Exception('Failed to create Node.js battle server')

            success = self.node_server.start()
            if not success:
                raise Exception('Failed to start Node.js battle server')

            self.debug_logger.info(f'Node.js服务器启动成功，端口: {self.node_server.port}')

            # 等待服务器就绪
            time.sleep(1)

            # 初始化战斗状态
            self._initialize_battle_state()

            logger.info(f'战斗引擎初始化完成 {self.battle_id} - 对战双方: {self.battle.player1.username} vs {self.battle.player2.username}')
            self.debug_logger.info(f'战斗引擎初始化完成 {self.battle_id} - 对战双方: {self.battle.player1.username} vs {self.battle.player2.username}')
            return True

        except Exception as e:
            error_msg = f'战斗引擎初始化异常: {e}'
            logger.error(error_msg)
            self.debug_logger.error(error_msg)
            
            # 尝试将错误信息保存到数据库
            try:
                if self.battle:
                    self.battle.status = 'error'
                    self.battle.error_message = error_msg
                    self.battle.finished_at = timezone.now()
                    self.battle.save(update_fields=['status', 'error_message', 'finished_at'])
                    self.debug_logger.info(f'已将错误信息保存到数据库: {error_msg}')
            except Exception as save_error:
                logger.error(f'保存错误信息到数据库失败: {save_error}')
                self.debug_logger.error(f'保存错误信息到数据库失败: {save_error}')
            
            return False

    def _get_battle_config(self) -> dict:
        """获取战斗配置"""
        # 判断是否为练习赛（通过battle_type字段或其他标识）
        battle_type = getattr(self.battle, 'battle_type', 'normal')
        if battle_type == 'practice' or self.battle.battle_type == 'practice':
            battle_type = 'practice'
        else:
            battle_type = 'normal'

        # 正常对战：为所有玩家名称添加后缀以避免重名
        players = [f'{self.battle.player1.username}(1)']

        # 处理玩家2
        if self.battle.player2:
            players.append(f'{self.battle.player2.username}(2)')
        else:
            # 如果没有玩家2，使用AI
            players.append('AI(2)')

        return {
            'map': self.battle.map_config.get('map', settings.BATTLE_ENGINE.get('DEFAULT_MAP', 'main')),  # 使用数据库中的地图配置
            'battleType': battle_type,
            'players': players,
            'round_limit': self.round_limit,
        }

    def _initialize_battle_state(self):
        """初始化战斗状态"""
        try:
            self.debug_logger.info('=== 开始初始化战斗状态 ===')
            self.debug_logger.info(f'战斗ID: {self.battle_id}')
            self.debug_logger.info(f'Node.js服务器端口: {self.node_server.port}')

            # 向Node.js服务器发送初始化请求
            config = self._get_battle_config()

            # 构建initCommand格式的数据
            init_command = {
                'player': settings.BATTLE_ENGINE.get('DEFAULT_PLAYER_SYSTEM', 'system'),
                'type': 'init',
                'round': 0,
                'battleId': self.battle_id,  # 添加战斗ID验证
                'param': {
                    'players': config.get('players', []),
                    'map': config.get('map', settings.BATTLE_ENGINE.get('DEFAULT_MAP', 'main')),
                    'round_limit': config.get('round_limit', self.round_limit),
                },
            }

            self.debug_logger.info('战斗配置详情:')
            self.debug_logger.info(f'  - 地图ID: {config.get("map")}')
            self.debug_logger.info(f'  - 战斗类型: {config.get("battleType")}')
            self.debug_logger.info(f'  - 最大回合数: {config.get("round_limit")}')
            self.debug_logger.info(f'  - 玩家1: {config.get("players", ["Unknown", "Unknown"])[0]}')
            self.debug_logger.info(f'  - 玩家2: {config.get("players", ["Unknown", "Unknown"])[1]}')

            url = f'http://localhost:{self.node_server.port}/battle/init'
            self.debug_logger.info(f'发送INIT命令到: {url}')
            self.debug_logger.info(f'INIT命令内容: {json.dumps(init_command, ensure_ascii=False, indent=2)}')

            response = requests.post(url, json=init_command, timeout=10)

            self.debug_logger.info(f'INIT命令响应状态码: {response.status_code}')

            if response.status_code == 200:
                response_data = None
                try:
                    response_data = response.json()
                    self.debug_logger.info(f'INIT命令响应内容: {json.dumps(response_data, ensure_ascii=False, indent=2)}')
                    self.debug_logger.info('✅ INIT命令执行成功')
                except:
                    self.debug_logger.info(f'INIT命令响应文本: {response.text}')
                    self.debug_logger.info('✅ INIT命令执行成功（响应非JSON格式）')

                try:
                    current_game_config = self.battle.game_config or {}
                    current_game_config['players'] = init_command.get('param', {}).get('players', [])
                    current_game_config['is_practice'] = config.get('battleType') == 'practice'

                    # 从初始化响应中获取基地最大血量
                    base_max_health = 600  # 默认值
                    if response_data and isinstance(response_data, dict):
                        base_health_info = response_data.get('baseHealthInfo', {})
                        if base_health_info:
                            # 从任意一个玩家的基地血量中获取最大血量（所有玩家的基地血量应该相同）
                            for player_id, health_info in base_health_info.items():
                                if isinstance(health_info, dict) and 'baseHealth' in health_info:
                                    base_max_health = health_info['baseHealth']
                                    self.debug_logger.info(f'从初始化响应中获取到基地血量: {base_max_health}')
                                    break

                    current_game_config['base_max_health'] = base_max_health

                    self.battle.game_config = current_game_config
                    # 保存init_command到数据库
                    self.battle.init_command = init_command
                    self.battle.save(update_fields=['game_config', 'init_command'])

                    self.debug_logger.info(f'✅ 已保存players信息到数据库: {init_command.get("param", {}).get("players", [])}')
                    self.debug_logger.info(f'✅ 已保存基地最大血量到数据库: {current_game_config["base_max_health"]}')
                    self.debug_logger.info(f'✅ 已保存init_command到数据库: {json.dumps(init_command, ensure_ascii=False)}')
                except Exception as save_error:
                    self.debug_logger.error(f'❌ 保存players信息失败: {save_error}')

            else:
                error_msg = f'❌ INIT命令执行失败: HTTP {response.status_code}, 响应: {response.text}'
                self.debug_logger.error(f'{error_msg}')

                # 检测battleId不匹配错误
                if response.status_code == 400 and 'BATTLE_ID_MISMATCH' in response.text:
                    self.debug_logger.log_error_with_context('初始化时检测到battleId不匹配', f'服务器响应: {response.text}')
                    # 触发Node.js服务器重启
                    if self._restart_node_server():
                        self.debug_logger.info('Node.js服务器重启成功，重新发送初始化命令')
                        # 重新发送初始化命令
                        try:
                            retry_response = requests.post(url, json=init_command, timeout=10)
                            if retry_response.status_code == 200:
                                self.debug_logger.info('重试初始化命令成功')
                                # 处理成功响应
                                try:
                                    response_data = retry_response.json()
                                    self.debug_logger.info(f'重试INIT命令响应内容: {json.dumps(response_data, ensure_ascii=False, indent=2)}')
                                except:
                                    self.debug_logger.info(f'重试INIT命令响应文本: {retry_response.text}')
                                return  # 成功处理，直接返回
                            else:
                                self.debug_logger.error(f'重试初始化命令失败: {retry_response.status_code}')
                        except Exception as retry_e:
                            self.debug_logger.error(f'重试初始化命令异常: {retry_e}')
                    else:
                        self.debug_logger.error('Node.js服务器重启失败')

                raise Exception(error_msg)

            logger.info('=== 战斗状态初始化完成 ===')
            self.debug_logger.info('=== 战斗状态初始化完成 ===')

        except Exception as e:
            error_msg = f'❌ INIT命令执行异常: {e}'
            logger.error(error_msg)
            self.debug_logger.error(f'{error_msg}')
            self.debug_logger.error(f'INIT异常堆栈: {traceback.format_exc()}')

            # 发送初始化失败告警
            send_battle_alert(
                alert_type=AlertType.SERVER_STARTUP_FAILURE,
                level=AlertLevel.CRITICAL,
                message=f"战斗 {self.battle_id} 初始化失败: {str(e)}",
                battle_id=self.battle_id,
                port=self.node_server.port if self.node_server else None,
                metadata={
                    'error': str(e),
                    'traceback': traceback.format_exc()
                }
            )

            raise

    def start_battle(self):
        """开始战斗主循环"""
        if self.is_running:
            logger.warning(f'战斗 {self.battle_id} 已在运行')
            return

        self.is_running = True
        self.debug_logger.info('开始执行start_battle方法')

        try:
            # 更新战斗状态
            self.debug_logger.info('更新战斗状态为running')
            self.battle.status = 'running'
            self.battle.started_at = timezone.now()
            self.battle.save()  # 保存到数据库
            self.debug_logger.info('战斗状态已保存到数据库')

            # 开始战斗主循环
            self.debug_logger.info('准备调用_battle_main_loop')
            self.debug_logger.info(f'调用前状态检查: is_running={self.is_running}')
            try:
                self.debug_logger.info('开始调用_battle_main_loop方法...')
                self._battle_main_loop()
                self.debug_logger.info('_battle_main_loop执行完成')
            except Exception as loop_error:
                self.debug_logger.error(f'_battle_main_loop执行异常: {loop_error}')
                self.debug_logger.error(f'主循环异常堆栈: {traceback.format_exc()}')
                raise
            finally:
                self.is_running = False
                self.debug_logger.info('_battle_main_loop执行完成，is_running设为False')

        except Exception as e:
            logger.error(f'战斗 {self.battle_id} 执行失败: {e}')
            self.debug_logger.error(f'战斗 {self.battle_id} 执行失败: {e}')
            self.debug_logger.error(f'异常堆栈: {traceback.format_exc()}')
            self._handle_battle_error(str(e))
        finally:
            self.debug_logger.info('start_battle方法执行完成')
            self.code_executor.cleanup()

    def _battle_main_loop(self):
        """战斗主循环"""
        self.debug_logger.info(f'开始主战斗循环，最大回合数: {self.round_limit}')
        self.debug_logger.info(f'循环条件检查: is_running={self.is_running}, round_limit={self.round_limit}')

        if not self.is_running:
            self.debug_logger.error(f'战斗 {self.battle_id} 未正在进行中...')
            return

        try:
            self.debug_logger.info('开始执行回合...')
            round_count = 0
            while self.is_running and round_count < self.round_limit:
                round_count += 1
                player2_name = self.battle.player2.username if self.battle.player2 else 'AI'
                player1_code_name = self.battle.player1_code.title if self.battle.player1_code else '无代码'
                player2_code_name = self.battle.player2_code.title if self.battle.player2_code else '无代码'
                process_id = os.getpid()
                self.debug_logger.info(f' [PID:{process_id}] 开始执行第 {round_count} 次循环, 在战斗 {self.battle_id} 【{self.battle.player1.username} ({player1_code_name}) 🎯 {player2_name} ({player2_code_name})')
                logger.info(f'[PID:{process_id}] 开始执行第 {round_count} 次循环, 在战斗 {self.battle_id} 【{self.battle.player1.username} ({player1_code_name}) 🎯 {player2_name} ({player2_code_name})】')

                # 记录回合开始时间
                round_start_time = time.time()
                self.debug_logger.info(f'循环开始时间: {time.strftime("%H:%M:%S", time.localtime(round_start_time))}')

                try:
                    # 执行一回合
                    round_result = self._execute_round()

                    # 保存回合数据
                    self._save_round_data(round_result)

                    # 检查是否有其他错误
                    if round_result.get('error') or not round_result.get('success', True):
                        error_type = round_result.get('error_type', 'unknown_error')
                        error_message = round_result.get('error', '未知错误')
                        error_details = round_result.get('error_details', {})

                        logger.error(f'战斗 {self.battle_id} 回合 {round_count} 发生其他错误: {error_message}')
                        self.debug_logger.error(f'其他错误，停止战斗: {error_message}')
                        self.debug_logger.error(f'错误类型: {error_type}')
                        self.debug_logger.error(f'错误详情: {error_details}')

                        # 构建详细的错误信息
                        detailed_error = {
                            'error_type': error_type,
                            'error_message': error_message,
                            'error_details': error_details,
                            'failed_round': round_count,
                            'battle_type': self.battle.battle_type,
                        }

                        # 根据战斗类型处理错误
                        if self.battle.battle_type == 'practice':
                            self._handle_practice_battle_error(detailed_error)
                        else:
                            self._handle_normal_battle_error(detailed_error)
                        break

                    # 检查战斗是否结束
                    battle_ended = self._check_battle_end(round_result)
                    if battle_ended:
                        winner = round_result.get('winner', '')
                        self.debug_logger.info(f'🏁 战斗正常结束，获胜者: {winner}')
                        break

                    # 记录回合结束状态
                    round_success = not round_result.get('error')
                    performance_info = ''
                    current_round = round_result.get('current_round', self._get_current_round())
                    if round_start_time:
                        round_duration = time.time() - round_start_time
                        performance_info = f'耗时{round_duration:.2f}秒'
                        self.debug_logger.log_performance(f'回合{current_round}', round_duration)

                    self.debug_logger.log_round_end(current_round, round_success, performance_info)

                    # 回合间隔
                    self.debug_logger.info('回合间隔等待...')

                except Exception as e:
                    # 在异常情况下，直接使用_get_current_round()获取回合号
                    current_round = self._get_current_round()
                    error_msg = f'Round {current_round} execution failed: {e}'
                    logger.error(traceback.format_exc())
                    self.debug_logger.error(error_msg)
                    self.debug_logger.error(f'回合执行异常堆栈: {traceback.format_exc()}')

                    # 根据战斗类型处理异常
                    detailed_error = {
                        'error_type': 'runtime_error',
                        'error_message': f'回合 {current_round} 执行异常: {str(e)}',
                        'error_details': {
                            'exception_type': type(e).__name__,
                            'exception_message': str(e),
                            'exception_stack': traceback.format_exc(),
                        },
                        'failed_round': current_round,
                        'battle_type': self.battle.battle_type,
                    }

                    if self.battle.battle_type == 'practice':
                        self.debug_logger.info('练习赛发生异常，进行错误处理')
                        self._handle_practice_battle_error(detailed_error)
                    else:
                        self.debug_logger.info('正常对战发生异常，进行错误处理')
                        self._handle_normal_battle_error(detailed_error)
                    break

            self.debug_logger.info(f'while循环结束，最终状态: is_running={self.is_running}, round_limit={self.round_limit}')

            # 检查战斗是否因错误而结束
            if self.battle.status == 'error':
                self.debug_logger.info('战斗因错误结束，跳过_finish_battle')
                return

            # 战斗正常结束
            self.debug_logger.info('开始执行_finish_battle')
            self._finish_battle()

        except Exception as e:
            self.debug_logger.error(f'_battle_main_loop异常: {e}')
            self.debug_logger.error(f'主循环异常堆栈: {traceback.format_exc()}')
            raise

        self.debug_logger.info('主战斗循环结束')
        self.debug_logger.info('_finish_battle执行完成')

    def _log_player_round_result(self, round_result: dict, player_idx: int):
        # 格式化打印player1_round_result
        self.debug_logger.info(f'=== 玩家{player_idx}回合结果详情 ===')
        self.debug_logger.info(f'成功状态: {round_result.get("success", "N/A")}')
        self.debug_logger.info(f'当前回合: {round_result.get("round", "N/A")}')
        self.debug_logger.info(f'战斗结束: {round_result.get("battle_ended", "N/A")}')
        self.debug_logger.info(f'获胜者: {round_result.get("winner", "N/A")}')
        self.debug_logger.info(f'失败命令数: {round_result.get("failedCommandCount", "N/A")}')
        self.debug_logger.info(f'错误信息: {round_result.get("error", "N/A")}')
        self.debug_logger.info(f'消息: {round_result.get("message", "N/A")}')
        # 打印执行详情
        execution_details = round_result.get('executionDetails', [])
        if execution_details:
            self.debug_logger.info(f'执行详情 ({len(execution_details)}条):')
            for i, detail in enumerate(execution_details[:5]):  # 只显示前5条避免日志过长
                self.debug_logger.info(f'  详情{i + 1}: {json.dumps(detail, ensure_ascii=False)}')
            if len(execution_details) > 5:
                self.debug_logger.info(f'  ... 还有{len(execution_details) - 5}条详情')
        # 打印战斗统计信息（简化版）
        battle_stats = round_result.get('battleStats', {})
        if battle_stats:
            self.debug_logger.info('战斗统计信息:')
            self.debug_logger.info(f'  回合数: {battle_stats.get("currentRound", "N/A")}')
            players = battle_stats.get('players', {})
            for player_id, stats in players.items():
                self.debug_logger.info(f'  玩家{player_id}: 资源={stats.get("rmb", "N/A")}')
        self.debug_logger.info(f'=== 玩家{player_idx}回合结果详情结束 ===')

    def _update_round_result(self, round_result: dict, player_id: str, player_round_result: dict, player_cmd_result: dict):
        player_failed_command_count = player_round_result.get('failedCommandCount', 0)
        player_execution_details = player_round_result.get('executionDetails', [])

        round_result['success'] = player_round_result.get('success', False)
        round_result['round'] = player_round_result.get('round', 0)
        round_result['battle_ended'] = player_round_result.get('battle_ended', False)
        round_result['winner'] = player_round_result.get('winner', None)
        round_result['battleStats'] = player_round_result.get('battleStats', {})
        round_result['endCommand'] = player_round_result.get('endCommand', None)
        round_result['message'] = player_round_result.get('message', '')
        round_result['error'] = player_round_result.get('error', '')

        # 错误记录给player
        if player_cmd_result and (player_cmd_result.get('error') or player_cmd_result.get('error_type') or player_cmd_result.get('error_details')):
            """
            in battle.js

            const detail: CommandExecutionDetailModel = {
                error: error,
                command: cmd,
                player: cmd.player,
                round: cmd.round,
                type: cmd.type,
                isError: isError,
                selfExecutionState: selfState,
                targetExecutionState: targetState
            };
            """
            error_info = [
                player_cmd_result.get('error', ''),
                player_cmd_result.get('error_type', ''),
                str(player_cmd_result.get('error_details', {})),
            ]
            error_info = '\n'.join(error_info)
            player_execution_details.append(
                {
                    'error': error_info,
                    'isError': True,
                }
            )

        if 'currentRoundFailedCommands' not in round_result:
            round_result['currentRoundFailedCommands'] = {}
            round_result['playerCommandExecutionDetails'] = {}
        round_result['currentRoundFailedCommands'][player_id] = player_failed_command_count
        round_result['playerCommandExecutionDetails'][player_id] = player_execution_details
        return round_result

    def _execute_round(self) -> dict:
        """执行一次循环 - 获取战斗状态，执行玩家脚本，发送命令"""
        self.debug_logger.info('=== 开始执行一次循环 ===')

        # 1. 获取当前战斗状态（包含当前回合信息）
        self.debug_logger.info('获取当前战斗状态...')
        battle_state_ret = self._get_battle_state()
        if not battle_state_ret:
            error_msg = '无法获取战斗状态'
            self.debug_logger.error(error_msg)
            return {'error': error_msg}

        # 从战斗状态中获取当前回合号（由游戏逻辑管理）
        battle_state = battle_state_ret.get('battleState', {})
        current_round = battle_state.get('currentRound', 1)
        self.debug_logger.info(f'🎯 从游戏逻辑获取的当前回合号: {current_round}')

        # 2. 获取玩家代码（已通过select_related预加载）
        self.debug_logger.info('获取玩家代码...')
        player1_code = self.battle.player1_code
        player2_code = self.battle.player2_code if self.battle.player2 else None

        # 判断是否为练习赛
        battle_type = getattr(self.battle, 'battle_type', 'normal')
        is_practice = battle_type == 'practice' or self.battle.battle_type == 'practice'

        # 3. 执行玩家1脚本获取命令
        config = self._get_battle_config()
        player1_id = config['players'][0]
        player2_id = config['players'][1]
        if is_practice:
            self.debug_logger.log_player_action('系统', f'练习赛模式，玩家ID: {player1_id}, {player2_id}')
        else:
            self.debug_logger.log_player_action('系统', f'对战模式，玩家ID: {player1_id}, {player2_id}')

        self.debug_logger.info('开始执行玩家1脚本...')
        player1_commands, player1_cmd_result, player1_script_input = self._execute_player_script(
            self.battle.player1, player1_code, battle_state, player1_id, current_round
        )

        # 检查玩家1代码执行是否失败
        player1_code_failed = not player1_cmd_result.get('success', True)
        if player1_code_failed:
            self.debug_logger.error(f'玩家1代码执行异常: {player1_cmd_result.get("error", "未知错误")}')

        player1_commands_data = {
            'playerID': player1_id,
            'commands': player1_commands,
            'battleId': self.battle_id,
            'codeExecutionFailed': player1_code_failed,
        }

        # 4. 发送玩家1命令到Node.js战斗服务器
        round_result = {}
        player1_round_result = self._process_commands(player1_commands_data)

        self._log_player_round_result(player1_round_result, 1)
        round_result = self._update_round_result({}, player1_id, player1_round_result, player1_cmd_result)

        self.debug_logger.info(f'发送命令到Node.js服务器: 玩家1={len(player1_commands)}个命令')

        # 取第一个玩家执行后获取最新的battleStats
        battle_state = round_result.get('battleStats', {})
        battleEnded = battle_state.get('battleEnded', False)

        # 初始化玩家2相关变量
        player2_commands = []
        player2_cmd_result = {'success': True}
        player2_script_input = {}
        player2_code_failed = False

        # 5. 执行玩家2脚本获取命令
        if self.battle.player2 and player2_code:
            # 正常对战：执行玩家2的脚本
            self.debug_logger.info('开始执行玩家2脚本...')
            player2_commands, player2_cmd_result, player2_script_input = self._execute_player_script(
                self.battle.player2, player2_code, battle_state, player2_id, current_round
            )
        elif is_practice:
            # 练习赛：使用玩家1的代码作为玩家2执行
            self.debug_logger.info('练习模式，使用玩家1代码执行玩家2脚本...')
            player2_commands, player2_cmd_result, player2_script_input = self._execute_player_script(
                self.battle.player1, player1_code, battle_state, player2_id, current_round
            )
        else:
            self.debug_logger.info('跳过玩家2脚本执行')

        # 检查玩家2代码执行是否失败
        player2_code_failed = not player2_cmd_result.get('success', True)
        if player2_code_failed:
            self.debug_logger.error(f'玩家2代码执行异常: {player2_cmd_result.get("error", "未知错误")}')

        # 6. 发送玩家2命令到Node.js战斗服务器
        player2_commands_data = {
            'playerID': player2_id,
            'commands': player2_commands,
            'battleId': self.battle_id,
            'codeExecutionFailed': player2_code_failed,
        }
        player2_round_result = self._process_commands(player2_commands_data)

        self._log_player_round_result(player2_round_result, 2)
        round_result = self._update_round_result(round_result, player2_id, player2_round_result, player2_cmd_result)

        self.debug_logger.info(f'发送命令到Node.js服务器:  玩家2={len(player2_commands)}个命令')

        self.debug_logger.info(f'循环执行完成，当前回合: {current_round}')

        round_result['current_round'] = current_round
        round_result['player_script_inputs'] = {player1_id: player1_script_input, player2_id: player2_script_input}
        return round_result

    def _get_current_round(self) -> int:
        """获取当前回合数 - 战斗结束后返回最后已知回合数，避免访问已停止的Node.js服务器"""
        # 如果战斗已结束，返回最后已知的回合数
        if self.battle_finished:
            self.debug_logger.info(f'战斗已结束，返回最后已知回合数: {self.last_known_round}')
            return self.last_known_round

        try:
            battle_state = self._get_battle_state()
            if battle_state and 'battleState' in battle_state:
                current_round = battle_state['battleState'].get('currentRound', 0)
                # 更新最后已知的回合数
                self.last_known_round = current_round
                return current_round
            return 0
        except Exception as e:
            self.debug_logger.warning(f'获取当前回合数失败: {e}')
            return self.last_known_round if self.last_known_round > 0 else 0

    def _get_battle_state(self) -> dict:
        """获取当前战斗状态"""
        # 如果战斗已结束，避免访问已停止的Node.js服务器
        if self.battle_finished:
            self.debug_logger.info('战斗已结束，跳过获取战斗状态请求')
            return {}

        try:
            url = f'http://localhost:{self.node_server.port}/battle/status'
            self.debug_logger.info('=== 获取战斗状态 ===')
            self.debug_logger.info(f'🌐 请求URL: {url}')
            request_start_time = time.time()
            response = requests.get(url, timeout=3)  # 减少超时时间提高响应性
            request_end_time = time.time()
            request_duration = (request_end_time - request_start_time) * 1000

            self.debug_logger.info(f'📡 请求完成，耗时: {request_duration:.2f}ms')
            self.debug_logger.info(f'📊 响应状态码: {response.status_code}')

            if response.status_code == 200:
                state = response.json()

                # 详细分析返回的状态
                self.debug_logger.info('✅ 成功获取战斗状态')
                self.debug_logger.info(f'📦 状态数据字段: {list(state.keys())}')

                # 检查battleState字段
                battle_state = state.get('battleState', {})
                if battle_state:
                    current_round = battle_state.get('currentRound', '未设置')
                    battle_ended = battle_state.get('battleEnded', '未设置')
                    state_status = battle_state.get('state', '未设置')

                    self.debug_logger.info('🔍 battleState详情:')
                    self.debug_logger.info(f'  - currentRound: {current_round}')
                    self.debug_logger.info(f'  - battleEnded: {battle_ended}')
                    self.debug_logger.info(f'  - state: {state_status}')

                    # 显示Node.js端的回合号
                    self.debug_logger.info(f'✅ Node.js端当前回合: {current_round}')
                else:
                    self.debug_logger.warning('⚠️ 响应中没有battleState字段')

                return state
            else:
                error_msg = f'Failed to get battle state: {response.text}'
                logger.error(error_msg)
                self.debug_logger.error('❌ 获取战斗状态失败')
                self.debug_logger.error(f'  状态码: {response.status_code}')
                self.debug_logger.error(f'  响应内容: {response.text}')
                return {}

        except Exception as e:
            error_msg = f'Error getting battle state: {e}'
            logger.error(error_msg)
            self.debug_logger.error(f'❌ 获取战斗状态异常: {error_msg}')
            return {}

    def _execute_player_script(
        self, player, code: Code, battle_state: dict, player_id: str, round_number: int = None
    ) -> tuple[list[dict], dict, dict]:
        """执行玩家脚本获取命令"""
        if round_number is None:
            round_number = self._get_current_round()

        try:
            self.debug_logger.info(f'=== 开始执行 {player.username} 的脚本 ===')
            self.debug_logger.info(f'🎯 执行回合号: {round_number}')
            self.debug_logger.info(f'玩家ID: {player_id}')
            self.debug_logger.info(f'回合超时设置: {self.round_timeout}秒')

            # 检查battleState中的回合信息
            battle_state_round = battle_state.get('currentRound', '未知')
            self.debug_logger.info(f'🔍 battleState中的回合号: {battle_state_round}')

            # battleState应该与当前执行的回合号一致
            expected_battle_state_round = round_number
            if battle_state_round != expected_battle_state_round:
                self.debug_logger.warning(
                    f'⚠️ battleState回合号异常! 期望: {expected_battle_state_round}, 实际: {battle_state_round}'
                )
            else:
                self.debug_logger.info(f'✅ battleState回合号正确: {battle_state_round}')

            # 准备脚本输入数据
            script_input = self._script_input_format(battle_state, player_id, round_number)

            self.debug_logger.info('📦 脚本输入数据准备完成:')
            self.debug_logger.info(f'  - battleState字段数: {len(battle_state)}')
            self.debug_logger.info(f'  - 传递给脚本的round字段: {round_number}')
            self.debug_logger.info(f'  - battleState.currentRound: {battle_state.get("currentRound", "未设置")}')
            # 详细打印battleState信息用于调试（仅在开关开启时）
            if self.verbose_battle_state:
                self.debug_logger.info(f'=== 传递给玩家 {player.username} 的battleState详情 ===')
                self.debug_logger.info(f'battleState完整内容: {json.dumps(battle_state, ensure_ascii=False, indent=2)}')
                self.debug_logger.info('=== battleState详情结束 ===')
            else:
                self.debug_logger.info(
                    f'传递给玩家 {player.username} 的battleState（详细信息已关闭，可设置verbose_battle_state=True开启）'
                )

            # 检查temp代码环境
            files_dict = None
            if self.code_executor.get_player_src_compiled_dir(player_id) is None:
                main_file = code.get_main_file()
                file_count = code.get_file_count()
                self.debug_logger.info(f'main文件项目: {main_file}，文件数量: {file_count}')
                files_dict = {}
                if file_count >= 1:
                    # 获取所有文件内容
                    for file_obj in code.files.all():
                        files_dict[file_obj.file_path] = file_obj.content
                        self.debug_logger.info(f'加载文件: {file_obj.file_path} ({len(file_obj.content)}字符)')
                else:
                    self.debug_logger.error('未找到可执行的代码文件')
                    # 修改result以反映异常状态
                    result = {}
                    result['success'] = False
                    result['error_type'] = 'code_execution_error'
                    result['error_details'] = {'reason': 'no_executable_file'}
                    result['error'] = '未找到可执行的代码文件'
                    return [], result, script_input

            # 执行多文件脚本，使用API模板
            self.debug_logger.info('开始执行脚本...')
            result = self.code_executor.execute_with_template(player_id, files_dict, code.language, json.dumps(script_input))

            # 执行脚本
            self.debug_logger.info(f'脚本执行完成: 成功={result["success"]}, 执行时间={result.get("execution_time", 0)}秒')

            # 记录完整的脚本输出
            script_output = result.get('output', '')
            self.debug_logger.info(f'脚本完整输出: {script_output}')

            # 记录控制台输出
            console_output = result.get('console_output', '')
            self.debug_logger.info(f'控制台完整输出: {console_output}')

            if result.get('error', ''):
                self.debug_logger.error(f'脚本错误: {result["error"]}')

            if result.get('error', '') or result.get('error_type', ''):
                print(result)

            if result['success']:
                # 检查是否有输出格式错误
                if result.get('error_type', ''):
                    error_msg = f'Player {player.username} output error'
                    self.debug_logger.error(error_msg)
                    self.debug_logger.error(result.get('error_details', {}))
                    # 修改result以反映错误状态
                    result['success'] = False
                    return [], result, script_input

                # 解析脚本输出为命令列表
                try:
                    if not result['output']:
                        # 空输出
                        warning_msg = f'Empty output from {player.username}'
                        self.debug_logger.warning(warning_msg)
                        result['success'] = True  # 空输出 但不一定异常
                        return [], result, script_input

                    commands = json.loads(result['output'])
                    if isinstance(commands, dict):
                        commands = [commands]

                    if isinstance(commands, list):
                        self.debug_logger.info(f'解析到 {len(commands)} 个命令')
                        # 详细记录每个命令
                        for i, cmd in enumerate(commands):
                            self.debug_logger.info(f'命令{i + 1}: {json.dumps(cmd, ensure_ascii=False)}')
                        return commands, result, script_input
                    else:
                        raise Exception('用户脚本输出非预期')

                except Exception as e:
                    warning_msg = f'Exception from {player.username}: {result["output"]}, 错误: {e}'
                    logger.warning(warning_msg)
                    self.debug_logger.warning(warning_msg)
                    result['error_type'] = 'output_format_error'
                    result['error_details'] = {
                        'exception_type': type(e).__name__,
                        'exception_message': str(e),
                        'exception_traceback': traceback.format_exc(),
                    }
                    result['success'] = False
                    result['error'] = '用户脚本执行错误'
                    return [], result, script_input

            # failed result
            else:
                error_msg = f'Script execution failed for {player.username}: {result["error"]}'
                logger.error(error_msg)
                self.debug_logger.error(error_msg)
                return [], result, script_input

        except Exception as e:
            error_msg = f'Error executing script for {player.username}: {e}'
            logger.error(error_msg)
            self.debug_logger.error(error_msg)
            # 创建错误结果对象
            result = {
                'success': False,
                'error': str(e),
                'error_type': 'execution_exception',
                'error_details': {
                    'exception_type': type(e).__name__,
                    'exception_message': str(e),
                    'exception_traceback': traceback.format_exc(),
                }
            }
            return [], result, {}

    def _script_input_format(self, battle_state: dict, player_id: str, round_number: int) -> dict:
        """将完整的battleState格式转换为精简的input_format格式

        Args:
            battle_state: 完整的战斗状态数据
            player_id: 当前玩家ID
            round_number: 当前回合数

        Returns:
            dict: 精简格式的脚本输入数据
        """
        try:
            # 创建精简的battleState，移除不必要的字段
            simplified_battle_state = {
                'status': battle_state.get('status'),
                'initialized': battle_state.get('initialized'),
                'battleEnded': battle_state.get('battleEnded'),
                'currentRound': battle_state.get('currentRound'),
                'players': {},
                'map': battle_state.get('map'),
            }

            # 处理players字段，移除stats字段
            original_players = battle_state.get('players', {})
            for pid, player_data in original_players.items():
                simplified_player = {
                    'id': player_data.get('id'),
                    'playerType': player_data.get('playerType'),
                    'rmb': player_data.get('rmb'),
                    'baseHealth': player_data.get('baseHealth'),
                    'units': player_data.get('units', []),
                    'buildings': player_data.get('buildings', []),
                }
                simplified_battle_state['players'][pid] = simplified_player

            # 构建最终的精简格式
            simplified_input = {
                'battleId': battle_state.get('battleId', self.battle_id),
                'playerId': player_id,
                'round': round_number,
                'timeLimit': self.round_timeout * 1000,  # 转换为毫秒
                'battleState': simplified_battle_state,
            }

            self.debug_logger.info('✅ 成功转换为精简格式，移除了stats等冗余字段')
            return simplified_input

        except Exception as e:
            self.debug_logger.error(f'❌ 转换为精简格式失败: {e}')
            # 如果转换失败，返回原始格式作为备用
            return {'battleState': battle_state, 'playerId': player_id, 'round': round_number, 'timeLimit': self.round_timeout}

    def _process_commands(self, player_commands: dict) -> dict:
        """处理命令并获取战斗结果"""

        try:
            self.debug_logger.info('=== 开始处理命令 ===')

            # 打印玩家命令统计
            player_id = player_commands.get('playerID', '')
            commands = player_commands.get('commands', [])
            self.debug_logger.log_player_action(player_id, f'提交了 {len(commands)} 个命令')
            if commands:
                self.debug_logger.debug(f'{player_id}命令详情:')
                for i, cmd in enumerate(commands):
                    self.debug_logger.debug(f'  命令{i + 1}: {json.dumps(cmd, ensure_ascii=False)}')
            else:
                self.debug_logger.debug(f'{player_id}: 无命令')

            url = f'http://localhost:{self.node_server.port}/battle/execute'

            # 记录请求开始时间
            start_time = time.time()

            response = requests.post(url, json=player_commands, timeout=self.round_timeout)

            # 记录请求结束时间和耗时
            end_time = time.time()
            duration = end_time - start_time
            self.debug_logger.log_performance('命令处理请求', duration, f'状态码: {response.status_code}')

            if response.status_code == 200:
                result = response.json()
                self.debug_logger.info(f'玩家 {player_id} ✅ 命令处理成功')

                if result.get('winner', None):
                    self.debug_logger.info(f'🏆 本回合产生获胜者: {result.get("winner", None)}')

                if result.get('error', None):
                    self.debug_logger.log_error_with_context(f'玩家 {player_id} 回合处理错误', result.get('error', None))

                self.debug_logger.info(f'=== 玩家 {player_id} 本回合命令处理完成 ===')
                return result
            else:
                error_msg = f'命令处理HTTP错误: {response.status_code}, 响应: {response.text}'
                logger.error(f'Failed to process commands: {response.text}')
                self.debug_logger.log_error_with_context('HTTP请求失败', error_msg)

                # 检测battleId不匹配错误
                if response.status_code == 400 and 'BATTLE_ID_MISMATCH' in response.text:
                    self.debug_logger.log_error_with_context('检测到battleId不匹配', f'服务器响应: {response.text}')
                    # 触发Node.js服务器重启
                    if self._restart_node_server():
                        self.debug_logger.info('Node.js服务器重启成功，重新发送命令')
                        # 重新发送命令
                        try:
                            url = f'http://localhost:{self.node_server.port}/battle/execute'
                            retry_response = requests.post(url, json=player_commands, timeout=self.round_timeout)
                            if retry_response.status_code == 200:
                                self.debug_logger.info('重试命令处理成功')
                                return retry_response.json()
                            else:
                                self.debug_logger.error(f'重试命令处理失败: {retry_response.status_code}')
                        except Exception as retry_e:
                            self.debug_logger.error(f'重试命令处理异常: {retry_e}')
                    else:
                        self.debug_logger.error('Node.js服务器重启失败')

                return {'error': 'Command in Node.js processing failed'}

        except requests.exceptions.Timeout as e:
            context = f'端口{self.node_server.port}, 数据大小{len(json.dumps(player_commands))}字符'
            self.debug_logger.log_error_with_context(f'请求超时(>{self.round_timeout}秒)', context)
            logger.error(f'Request timeout: {e}')

            # 发送超时告警
            send_battle_alert(
                alert_type=AlertType.CONNECTION_ERROR,
                level=AlertLevel.WARNING,
                message=f"战斗 {self.battle_id} 命令处理请求超时",
                battle_id=self.battle_id,
                port=self.node_server.port,
                metadata={
                    'timeout_seconds': self.round_timeout,
                    'data_size': len(json.dumps(player_commands)),
                    'error': str(e)
                }
            )

            return {'error': f'Request Node.js timeout after {self.round_timeout} seconds'}
        except requests.exceptions.ConnectionError as e:
            context = f'端口{self.node_server.port}, 请检查Node.js服务器状态'
            self.debug_logger.log_error_with_context('连接Node.js服务器失败', context)
            logger.error(f'Connection error: {e}')

            # 发送连接错误告警
            send_battle_alert(
                alert_type=AlertType.CONNECTION_ERROR,
                level=AlertLevel.ERROR,
                message=f"战斗 {self.battle_id} 连接Node.js服务器失败",
                battle_id=self.battle_id,
                port=self.node_server.port,
                metadata={'error': str(e)}
            )

            return {'error': 'Connection to Node.js server failed'}
        except Exception as e:
            current_round = self._get_current_round()
            context = f'回合{current_round}, 异常类型{type(e).__name__}'
            self.debug_logger.log_error_with_context('命令处理异常', context, exc_info=True)
            logger.error(f'Error processing commands: {e}')
            self.debug_logger.error(f'命令处理异常堆栈: {traceback.format_exc()}')
            return {'error': str(e)}

    def _restart_node_server(self) -> bool:
        """重启Node.js服务器"""
        try:
            self.debug_logger.info('开始重启Node.js服务器...')

            # 发送服务器重启告警
            send_battle_alert(
                alert_type=AlertType.BATTLE_ID_MISMATCH,
                level=AlertLevel.WARNING,
                message=f"战斗 {self.battle_id} 检测到battleId不匹配，正在重启Node.js服务器",
                battle_id=self.battle_id,
                metadata={'action': 'server_restart_initiated'}
            )

            # 获取服务器管理器
            manager = get_node_server_manager()

            # 停止当前服务器
            old_port = None
            if self.node_server:
                old_port = self.node_server.port
                self.debug_logger.info(f'停止当前服务器，端口: {old_port}')
                manager.stop_battle_server(self.battle_id)

            # 创建新的服务器实例
            self.debug_logger.info('创建新的服务器实例...')
            self.node_server = manager.create_battle_server(self.battle_id, self._get_battle_config())
            if not self.node_server:
                self.debug_logger.error('创建新服务器实例失败')
                send_battle_alert(
                    alert_type=AlertType.SERVER_STARTUP_FAILURE,
                    level=AlertLevel.ERROR,
                    message=f"战斗 {self.battle_id} 创建新服务器实例失败",
                    battle_id=self.battle_id,
                    port=old_port,
                    metadata={'action': 'create_server_failed'}
                )
                return False

            # 启动新服务器
            new_port = self.node_server.port
            self.debug_logger.info(f'启动新服务器，端口: {new_port}')
            success = self.node_server.start()
            if not success:
                self.debug_logger.error('启动新服务器失败')
                send_battle_alert(
                    alert_type=AlertType.SERVER_STARTUP_FAILURE,
                    level=AlertLevel.ERROR,
                    message=f"战斗 {self.battle_id} 启动新服务器失败",
                    battle_id=self.battle_id,
                    port=new_port,
                    metadata={'action': 'start_server_failed'}
                )
                return False

            # 等待服务器就绪
            time.sleep(2)

            # 重新初始化战斗状态
            self.debug_logger.info('重新初始化战斗状态...')
            self._initialize_battle_state()

            self.debug_logger.info('Node.js服务器重启成功')

            # 发送重启成功告警
            send_battle_alert(
                alert_type=AlertType.BATTLE_ID_MISMATCH,
                level=AlertLevel.INFO,
                message=f"战斗 {self.battle_id} Node.js服务器重启成功",
                battle_id=self.battle_id,
                port=new_port,
                metadata={
                    'action': 'server_restart_success',
                    'old_port': old_port,
                    'new_port': new_port
                }
            )

            return True

        except Exception as e:
            self.debug_logger.error(f'重启Node.js服务器失败: {e}')
            send_battle_alert(
                alert_type=AlertType.SERVER_STARTUP_FAILURE,
                level=AlertLevel.CRITICAL,
                message=f"战斗 {self.battle_id} 重启Node.js服务器异常: {str(e)}",
                battle_id=self.battle_id,
                metadata={'action': 'server_restart_exception', 'error': str(e)}
            )
            return False

    def _save_round_data(self, round_result: dict):
        """保存回合数据"""
        try:
            self.debug_logger.info('保存回合数据...')

            current_round_failed = round_result.get('currentRoundFailedCommands', {})

            config = self._get_battle_config()
            player1_id = config['players'][0]
            player2_id = config['players'][1]

            # 从 round_result 中获取所有命令执行详情
            player_command_execution_details = round_result.get('playerCommandExecutionDetails', {})
            player1_execution_details = player_command_execution_details.get(player1_id, [])
            player2_execution_details = player_command_execution_details.get(player2_id, [])

            # 获取执行过的命令集合（只存成功的命令）
            player1_commands = [
                detail.get('command', {}) for detail in player1_execution_details if not detail.get('isError', True)
            ]
            player2_commands = [
                detail.get('command', {}) for detail in player2_execution_details if not detail.get('isError', True)
            ]

            # 获取当前回合失败命令数
            player1_failed = current_round_failed.get(player1_id, 0)
            player2_failed = current_round_failed.get(player2_id, 0)

            # 计算当前回合成功命令数
            player1_successful = len(player1_commands) - player1_failed
            player2_successful = len(player2_commands) - player2_failed

            # 确保统计数据不为负数
            player1_successful = max(0, player1_successful)
            player1_failed = max(0, player1_failed)
            player2_successful = max(0, player2_successful)
            player2_failed = max(0, player2_failed)

            # 根据 isError 字段筛选失败命令详情
            player1_failed_detail = [detail for detail in player1_execution_details if detail.get('isError', True)]
            player2_failed_detail = [detail for detail in player2_execution_details if detail.get('isError', True)]

            current_round = round_result.get('current_round')
            if current_round is None:
                current_round = self._get_current_round()
                self.debug_logger.warning(f'round_result中没有current_round字段，回退到_get_current_round(): {current_round}')
            else:
                self.debug_logger.info(f'使用round_result中的回合号: {current_round}')

            # 检查是否有报错或失败命令，如果有则保存对应玩家的battle_state
            player1_battle_state = None
            player2_battle_state = None

            # 检查玩家1是否有错误或失败命令
            player1_has_error = (
                player1_failed > 0
                or len(player1_failed_detail) > 0
                or round_result.get('error')
                or any(detail.get('isError', False) for detail in player1_execution_details)
            )

            # 检查玩家2是否有错误或失败命令
            player2_has_error = (
                player2_failed > 0
                or len(player2_failed_detail) > 0
                or round_result.get('error')
                or any(detail.get('isError', False) for detail in player2_execution_details)
            )

            # 根据battle.save_round_inputs设置决定是否保存script_input
            if self.battle.save_round_inputs:
                # 如果开启了保存回合输入数据，则保存所有玩家的script_input
                player1_battle_state = round_result.get('player_script_inputs', {}).get(player1_id, {})
                player2_battle_state = round_result.get('player_script_inputs', {}).get(player2_id, {})
                self.debug_logger.info(f'回合{current_round}保存所有玩家的script_input（开启了保存回合输入数据）')
            else:
                # 如果未开启保存回合输入数据，则只在有错误或失败命令时保存script_input
                if player1_has_error:
                    player1_battle_state = round_result.get('player_script_inputs', {}).get(player1_id, {})
                    self.debug_logger.info(f'玩家1在回合{current_round}有错误或失败命令，保存其script_input')
                    self.debug_logger.info(f'玩家1失败命令数: {player1_failed}, 错误详情数: {len(player1_failed_detail)}')

                if player2_has_error:
                    player2_battle_state = round_result.get('player_script_inputs', {}).get(player2_id, {})
                    self.debug_logger.info(f'玩家2在回合{current_round}有错误或失败命令，保存其script_input')
                    self.debug_logger.info(f'玩家2失败命令数: {player2_failed}, 错误详情数: {len(player2_failed_detail)}')

            round_data = BattleRound(
                battle=self.battle,
                round_number=current_round,  # 当前回合
                round_data=round_result,  # 回合数据
                player1_units=round_result.get('player1_units', []),  # 玩家1 单位状态 (目前没加)
                player2_units=round_result.get('player2_units', []),  # 玩家2 单位状态 (目前没加)
                player1_actions=player1_commands,  # 玩家1 本回合命令集
                player2_actions=player2_commands,  # 玩家2 本回合命令集
                player1_successful_commands=player1_successful,  # 玩家1 本回合成功命令数
                player1_failed_commands=player1_failed,  # 玩家1 本回合失败命令数
                player2_successful_commands=player2_successful,  # 玩家2 本回合成功命令数
                player2_failed_commands=player2_failed,  # 玩家2 本回合失败命令数
                player1_failed_commands_detail=player1_failed_detail,  # 玩家1 本回合失败命令详情
                player2_failed_commands_detail=player2_failed_detail,  # 玩家2 本回合失败命令详情
                player1_command_execution_details=player1_execution_details,  # 玩家1 本回合所有命令执行详情
                player2_command_execution_details=player2_execution_details,  # 玩家2 本回合所有命令执行详情
                player1_battle_state=player1_battle_state,  # 玩家1 script_input（仅在有错误时保存）
                player2_battle_state=player2_battle_state,  # 玩家2 script_input（仅在有错误时保存）
                timestamp=timezone.now(),
            )

            retry_save_operation(lambda: round_data.save(), debug_logger=self.debug_logger, operation_name='回合数据保存')

            # 实时保存玩家统计信息
            battle_state = round_result.get('battleStats', {})
            players_info = battle_state.get('players', {})
            if players_info:
                self.debug_logger.info(f'回合{current_round}更新玩家统计信息...')

                # 保存玩家1统计信息
                if player1_id in players_info:
                    player1_info = players_info[player1_id]
                    self.battle.player1_stats = {
                        'rmb': player1_info.get('rmb', 0),
                        'baseHealth': player1_info.get('baseHealth', 0),
                        'stats': player1_info.get('stats', {}),
                    }
                    self.debug_logger.info(f'回合{current_round}玩家1统计信息: {self.battle.player1_stats}')

                # 保存玩家2统计信息
                if player2_id in players_info:
                    player2_info = players_info[player2_id]
                    self.battle.player2_stats = {
                        'rmb': player2_info.get('rmb', 0),
                        'baseHealth': player2_info.get('baseHealth', 0),
                        'stats': player2_info.get('stats', {}),
                    }
                    self.debug_logger.info(f'回合{current_round}玩家2统计信息: {self.battle.player2_stats}')

                # 保存玩家统计信息到数据库
                retry_save_operation(
                    lambda: self.battle.save(update_fields=['player1_stats', 'player2_stats']),
                    debug_logger=self.debug_logger,
                    operation_name='玩家统计信息保存'
                )

            battle_ended = round_result.get('battle_ended', False)
            end_command = round_result.get('endCommand', {})
            if battle_ended and end_command:
                self.battle.end_command = end_command
                self.debug_logger.info(f'保存结束命令: {end_command}')
                self.battle.save(update_fields=['end_command'])

            self.debug_logger.info(
                f'回合 {current_round} 数据保存成功: P1({player1_successful}成功/{player1_failed}失败), P2({player2_successful}成功/{player2_failed}失败)'
            )

        except Exception as e:
            logger.error(f'保存回合数据失败: {e}')
            self.debug_logger.error(f'保存回合数据失败: {e}')

    def _check_battle_end(self, round_result: dict) -> bool:
        """检查战斗是否结束"""
        self.debug_logger.info('=== 检查战斗结束条件 ===')
        current_round = round_result.get('current_round')
        if current_round is None:
            current_round = self._get_current_round()

        self.debug_logger.info(f'当前回合: {current_round}/{self.round_limit}')



        # 检查是否有玩家获胜
        if round_result.get('winner', None) or round_result.get('battleEnded', False):
            winner = round_result['winner']
            self.debug_logger.info('🏆 战斗结束原因: 玩家获胜')
            self.debug_logger.info(f'🏆 获胜者: {winner}')
            battleStats = round_result.get('battleStats', {})
            battleResult = battleStats.get('battleResult', {})
            if battleResult:
                self.debug_logger.info(f'🏆 详细结束原因: {battleResult.get("msg", "")}')
                playerStats = battleResult.get('playerStats', {})
                if playerStats:
                    self.debug_logger.info(f'🏆 战斗统计: {json.dumps(playerStats, ensure_ascii=False, indent=2)}')
            return True

        # 检查是否有错误
        if round_result.get('error', None):
            error = round_result['error']
            self.debug_logger.info('❌ 战斗结束原因: 发生错误')
            self.debug_logger.info(f'❌ 错误信息: {error}')
            return True

        self.debug_logger.info('✅ 战斗继续进行')
        return False

    def _finish_battle(self):
        """结束战斗"""
        try:
            self.debug_logger.info('=== 开始结束战斗处理 ===')
            self.debug_logger.info(f'战斗ID: {self.battle_id}')

            # 获取最终战斗状态
            self.debug_logger.info('获取最终战斗状态...')
            final_state = self._get_battle_state().get('battleState', {})

            current_round = final_state.get('currentRound', 0)
            self.debug_logger.info(f'总回合数: {current_round}')

            # 标记战斗已结束，避免后续访问Node.js服务器
            self.battle_finished = True
            self.debug_logger.info('✅ 已标记战斗结束状态，后续将避免访问Node.js服务器')

            # 确定战斗结果
            winner = ''

            # 从Node.js服务器获取战斗结果
            battleResult = final_state.get('battleResult', None)
            if battleResult:
                winner = battleResult.get('winner', '')
            self.debug_logger.info(f'从Node.js服务器获取获胜者: {winner if winner else "平局"}')

            self.debug_logger.info(f'最终战斗获胜者: {winner if winner else "平局"}')

            # 获取当前战斗配置中的玩家ID
            config = self._get_battle_config()
            player1_id = config['players'][0]
            player2_id = config['players'][1]

            # 提取玩家统计信息
            players_info = final_state.get('players', {})

            # 保存玩家统计信息
            if player1_id in players_info:
                player1_info = players_info[player1_id]
                self.battle.player1_stats = {
                    'rmb': player1_info.get('rmb', 0),
                    'baseHealth': player1_info.get('baseHealth', 0),
                    'stats': player1_info.get('stats', {}),
                }
                self.debug_logger.info(f'玩家1统计信息: {self.battle.player1_stats}')

            if player2_id in players_info:
                player2_info = players_info[player2_id]
                self.battle.player2_stats = {
                    'rmb': player2_info.get('rmb', 0),
                    'baseHealth': player2_info.get('baseHealth', 0),
                    'stats': player2_info.get('stats', {}),
                }
                self.debug_logger.info(f'玩家2统计信息: {self.battle.player2_stats}')

            # 计算玩家分数
            # 判断每个玩家的胜负状态：True=胜利, False=失败, None=平局
            if winner == player1_id:
                player1_result = True
                player2_result = False
            elif winner == player2_id:
                player1_result = False
                player2_result = True
            else:
                # 平局情况
                player1_result = None
                player2_result = None

            self.battle.player1_score = self._calculate_player_score(self.battle.player1_stats, player1_result)
            self.battle.player2_score = self._calculate_player_score(self.battle.player2_stats, player2_result)

            if winner == player1_id:
                self.battle.result = 'player1_win'
                self.debug_logger.info(
                    f'🏆 玩家1({player1_id})获胜! 分数: P1={self.battle.player1_score}, P2={self.battle.player2_score}'
                )
            elif winner == player2_id:
                self.battle.result = 'player2_win'
                self.debug_logger.info(
                    f'🏆 玩家2({player2_id})获胜! 分数: P1={self.battle.player1_score}, P2={self.battle.player2_score}'
                )
            else:
                self.battle.result = 'draw'
                self.debug_logger.info(f'🤝 战斗平局! 分数: P1={self.battle.player1_score}, P2={self.battle.player2_score}')

            # 更新战斗状态
            self.debug_logger.info('更新战斗数据库状态...')
            self.battle.status = 'finished'
            self.battle.finished_at = timezone.now()
            self.battle.total_rounds = current_round

            self.debug_logger.info(f'战斗结果: {self.battle.result}')
            self.debug_logger.info(f'开始时间: {self.battle.started_at}')
            self.debug_logger.info(f'结束时间: {self.battle.finished_at}')

            retry_save_operation(lambda: self.battle.save(), debug_logger=self.debug_logger, operation_name='战斗结束状态保存')
            self.debug_logger.info('✅ 战斗数据库状态更新完成（统计数据将通过信号自动更新）')

            self.debug_logger.info(f'🎉 战斗 {self.battle_id} 正常结束')
            self.debug_logger.info(f'🎉 最终结果: {self.battle.result}')
            self.debug_logger.info(f'🎉 总回合数: {current_round}')
            self.debug_logger.info('=== 战斗结束处理完成 ===')

            # 停止并清理Node.js服务器
            self.debug_logger.info('停止Node.js服务器...')
            if self.node_server:
                try:
                    self.node_server.stop()
                    from .node_server_manager import get_node_server_manager

                    manager = get_node_server_manager()
                    manager.stop_battle_server(self.battle_id)
                    self.debug_logger.info('✅ Node.js服务器已停止')
                except Exception as e:
                    error_msg = f'停止Node.js服务器失败: {e}'
                    logger.error(f'Error stopping node server for battle {self.battle_id}: {e}')
                    self.debug_logger.error(error_msg)

            # 清理debug_logger的文件处理器
            self.debug_logger.info('清理日志记录器...')
            if hasattr(self, 'debug_logger') and self.debug_logger:
                try:
                    for handler in self.debug_logger.handlers[:]:
                        handler.close()
                        self.debug_logger.removeHandler(handler)
                except Exception as e:
                    logger.error(f'Error cleaning up debug logger for battle {self.battle_id}: {e}')

            # 根据战斗结果显示胜利者信息
            if self.battle.result == 'player1_win':
                winner_info = f'{self.battle.player1.username}'
            elif self.battle.result == 'player2_win':
                winner_info = f'{self.battle.player2.username}'
            else:
                winner_info = '平局'

            player1_code_name = self.battle.player1_code.title if self.battle.player1_code else '无代码'
            player2_code_name = self.battle.player2_code.title if self.battle.player2_code else '无代码'
            logger.info(f'战斗 {self.battle_id} 结束，结果: {self.battle.result}，胜利者: {winner_info} [代码: {player1_code_name} vs {player2_code_name}]')

        except Exception as e:
            error_msg = f'❌ 结束战斗处理异常: {e}'
            logger.error(f'{error_msg}')
            self.debug_logger.error(f'{error_msg}')
            self.debug_logger.error(f'结束战斗异常堆栈: {traceback.format_exc()}')
            self._handle_battle_error(str(e))

    def _calculate_player_score(self, player_stats, is_winner):
        """
        基于玩家统计数据计算分数

        分数计算公式:
        基础分 = 胜利奖励(胜利+50, 平局+25, 失败+10)
        战斗分 = 击杀数*10 - 死亡数*5 + 伤害输出/100 + 治疗量/50
        经济分 = RMB/10 + 基地血量/20
        效率分 = 100 - 命令失败数*2 (最低0分)

        总分 = 基础分 + 战斗分 + 经济分 + 效率分
        """
        if not player_stats:
            return 0

        stats = player_stats.get('stats', {})

        # 基础分数 - 根据胜负情况
        if is_winner:
            base_score = 50  # 胜利奖励
        elif is_winner is None:  # 平局
            base_score = 25
        else:
            base_score = 10  # 失败基础分

        # 战斗分数
        kills = stats.get('kills', 0)
        deaths = stats.get('deaths', 0)
        damage_dealt = stats.get('totalDamageDealt', 0)
        healing = stats.get('totalHealing', 0)

        battle_score = (
            kills * 10  # 每击杀10分
            - deaths * 5  # 每死亡扣5分
            + damage_dealt / 100  # 伤害转换为分数
            + healing / 50  # 治疗转换为分数
        )

        # 经济分数
        rmb = player_stats.get('rmb', 0)
        base_health = player_stats.get('baseHealth', 0)

        economic_score = (
            rmb / 10  # RMB转换为分数
            + base_health / 20  # 基地血量转换为分数
        )

        # 效率分数
        command_fail_count = stats.get('commandFailCount', 0)
        efficiency_score = max(0, 100 - command_fail_count * 2)  # 命令失败扣分，最低0分

        # 总分计算
        total_score = base_score + battle_score + economic_score + efficiency_score

        # 确保分数为非负整数
        final_score = max(0, int(total_score))

        self.debug_logger.info(
            f'分数计算详情: 基础分={base_score}, 战斗分={battle_score:.1f}, '
            f'经济分={economic_score:.1f}, 效率分={efficiency_score}, 总分={final_score}'
        )

        return final_score

    def _update_player_statistics(self):
        """更新玩家统计数据"""
        try:
            # 检查是否是自己对战自己，如果是则跳过统计数据更新
            if self.battle.player1 == self.battle.player2:
                self.debug_logger.info('检测到自己对战自己，跳过统计数据更新')
                return

            # 更新玩家1统计数据
            if self.battle.player1_stats:
                player1_stats = self.battle.player1_stats
                detailed_stats = {
                    'kills': player1_stats.get('stats', {}).get('kills', 0),
                    'deaths': player1_stats.get('stats', {}).get('deaths', 0),
                    'totalDamageDealt': player1_stats.get('stats', {}).get('totalDamageDealt', 0),
                    'totalDamageReceived': player1_stats.get('stats', {}).get('totalDamageReceived', 0),
                    'totalHealing': player1_stats.get('stats', {}).get('totalHealing', 0),
                    'commandFailCount': player1_stats.get('stats', {}).get('commandFailCount', 0),
                    'rmb': player1_stats.get('rmb', 0),
                    'baseHealth': player1_stats.get('baseHealth', 0),
                }

                # 获取或创建玩家1的统计记录
                player1_battle_stats, created = BattleStatistics.objects.get_or_create(user=self.battle.player1)
                player1_battle_stats.update_detailed_stats(detailed_stats)
                self.debug_logger.info('玩家1统计数据已更新')

            # 更新玩家2统计数据
            if self.battle.player2_stats:
                player2_stats = self.battle.player2_stats
                detailed_stats = {
                    'kills': player2_stats.get('stats', {}).get('kills', 0),
                    'deaths': player2_stats.get('stats', {}).get('deaths', 0),
                    'totalDamageDealt': player2_stats.get('stats', {}).get('totalDamageDealt', 0),
                    'totalDamageReceived': player2_stats.get('stats', {}).get('totalDamageReceived', 0),
                    'totalHealing': player2_stats.get('stats', {}).get('totalHealing', 0),
                    'commandFailCount': player2_stats.get('stats', {}).get('commandFailCount', 0),
                    'rmb': player2_stats.get('rmb', 0),
                    'baseHealth': player2_stats.get('baseHealth', 0),
                }

                # 获取或创建玩家2的统计记录
                player2_battle_stats, created = BattleStatistics.objects.get_or_create(user=self.battle.player2)
                player2_battle_stats.update_detailed_stats(detailed_stats)
                self.debug_logger.info('玩家2统计数据已更新')

        except Exception as e:
            error_msg = f'更新玩家统计数据失败: {e}'
            logger.error(error_msg)
            self.debug_logger.error(error_msg)
            self.debug_logger.error(f'统计数据更新异常堆栈: {traceback.format_exc()}')

    def _handle_battle_error(self, error_message: str):
        """处理战斗错误"""
        try:
            self.battle.status = 'error'
            self.battle.error_message = error_message
            self.battle.finished_at = timezone.now()
            retry_save_operation(lambda: self.battle.save(), debug_logger=self.debug_logger, operation_name='战斗错误状态保存')

            # 停止Node.js服务器
            if self.node_server:
                self.node_server.stop()

            # 清理debug_logger的文件处理器
            if hasattr(self, 'debug_logger') and self.debug_logger:
                try:
                    for handler in self.debug_logger.handlers[:]:
                        handler.close()
                        self.debug_logger.removeHandler(handler)
                    logger.info(f'战斗 {self.battle_id} 错误处理完成，日志记录器已清理')
                except Exception as e:
                    logger.error(f'Error cleaning up debug logger for battle {self.battle_id}: {e}')

        except Exception as e:
            logger.error(f'Error handling battle error: {e}')

    def _handle_normal_battle_error(self, error_info: dict):
        """处理正常对战的错误"""
        try:
            error_type = error_info.get('error_type', 'unknown_error')
            error_message = error_info.get('error_message', '未知错误')
            error_details = error_info.get('error_details', {})
            failed_round = error_info.get('failed_round', self._get_current_round())

            logger.error(f'Normal battle {self.battle_id} failed at round {failed_round}: {error_message}')

            # 构建详细的错误信息
            detailed_error_info = {
                'error_type': error_type,
                'error_message': error_message,
                'error_details': error_details,
                'failed_round': failed_round,
                'error_time': timezone.now().isoformat(),
                'battle_type': 'normal',
            }

            # 根据错误类型生成用户友好的错误描述
            user_friendly_message = self._get_user_friendly_error_message(error_type, error_message, error_details)

            # 更新战斗状态
            self.battle.status = 'error'
            self.battle.error_message = user_friendly_message
            self.battle.finished_at = timezone.now()

            # 保存详细错误信息
            if not self.battle.map_config:
                self.battle.map_config = {}
            self.battle.map_config['error_info'] = detailed_error_info

            retry_save_operation(
                lambda: self.battle.save(), debug_logger=self.debug_logger, operation_name='正常对战错误状态保存'
            )

            # 停止战斗引擎和清理资源
            self.is_running = False
            self.stop_battle()

        except Exception as e:
            logger.error(f'Failed to handle normal battle error: {e}')

    def _handle_practice_battle_error(self, error_info: dict):
        """处理练习赛错误"""
        try:
            error_type = error_info.get('error_type', 'unknown_error')
            error_message = error_info.get('error_message', '未知错误')
            error_details = error_info.get('error_details', {})
            failed_round = error_info.get('failed_round', self._get_current_round())

            logger.info(f'Practice battle {self.battle_id} failed at round {failed_round}: {error_message}')

            # 构建详细的错误信息
            detailed_error_info = {
                'error_type': error_type,
                'error_message': error_message,
                'error_details': error_details,
                'failed_round': failed_round,
                'error_time': timezone.now().isoformat(),
                'battle_type': 'practice',
            }

            # 根据错误类型生成用户友好的错误描述
            user_friendly_message = self._get_user_friendly_error_message(error_type, error_message, error_details)

            # 更新战斗状态为错误
            self.battle.status = 'error'
            self.battle.error_message = user_friendly_message
            self.battle.finished_at = timezone.now()

            # 保存详细错误信息到battle的额外字段
            if not self.battle.map_config:
                self.battle.map_config = {}
            self.battle.map_config['practice_error'] = detailed_error_info

            retry_save_operation(
                lambda: self.battle.save(), debug_logger=self.debug_logger, operation_name='练习赛错误状态保存'
            )

            # 停止战斗引擎和清理资源
            self.is_running = False
            self.stop_battle()

        except Exception as e:
            logger.error(f'Failed to handle practice battle error: {e}')

    def _get_user_friendly_error_message(self, error_type: str, error_message: str, error_details: dict) -> str:
        """根据错误类型生成用户友好的错误描述"""
        from .code_executor import CodeExecutionErrorTypes

        if error_type == CodeExecutionErrorTypes.TIMEOUT:
            timeout_seconds = error_details.get('timeout_seconds', '未知')
            language = error_details.get('language', '未知')
            return f'代码执行超时：{language}代码执行时间超过{timeout_seconds}秒限制'

        elif error_type == CodeExecutionErrorTypes.SYNTAX_ERROR:
            language = error_details.get('language', '未知')
            return f'代码语法错误：{language}代码存在语法错误，请检查代码语法'

        elif error_type == CodeExecutionErrorTypes.RUNTIME_ERROR:
            language = error_details.get('language', '未知')
            return f'代码运行时错误：{language}代码在执行过程中发生错误'

        elif error_type == CodeExecutionErrorTypes.OUTPUT_FORMAT_ERROR:
            exception_message = error_details.get('exception_message', '')
            if 'empty_output' in error_details.get('reason', ''):
                return '代码输出格式错误：代码执行成功但没有输出任何命令'
            else:
                return f'代码输出格式错误：输出不是有效的JSON格式 - {exception_message}'

        elif error_type == CodeExecutionErrorTypes.COMMAND_FORMAT_ERROR:
            reason = error_details.get('reason', '')
            if 'not_array_or_object' in reason:
                output_type = error_details.get('output_type', '未知')
                return f'命令格式错误：期望输出命令数组或对象，实际输出为{output_type}类型'
            else:
                return '命令格式错误：输出的命令格式不正确'

        elif error_type == CodeExecutionErrorTypes.COMPILATION_ERROR:
            language = error_details.get('language', '未知')
            return f'代码编译错误：{language}代码编译失败，请检查代码语法'

        elif error_type == CodeExecutionErrorTypes.IMPORT_ERROR:
            language = error_details.get('language', '未知')
            return f'模块导入错误：{language}代码中导入的模块不存在或不可用'

        elif error_type == CodeExecutionErrorTypes.MEMORY_ERROR:
            return '内存错误：代码执行时内存不足'

        elif error_type == CodeExecutionErrorTypes.PERMISSION_ERROR:
            return '权限错误：代码执行时权限不足'

        else:
            return f'未知错误：{error_message}'

    def stop_battle(self):
        """停止战斗"""
        self.is_running = False

        # 停止并清理Node.js服务器
        if self.node_server:
            try:
                self.node_server.stop()
                # 确保从管理器中移除服务器实例
                from .node_server_manager import get_node_server_manager

                manager = get_node_server_manager()
                manager.stop_battle_server(self.battle_id)
            except Exception as e:
                logger.error(f'Error stopping node server for battle {self.battle_id}: {e}')

        # 清理debug_logger的文件处理器
        if hasattr(self, 'debug_logger') and self.debug_logger:
            try:
                for handler in self.debug_logger.handlers[:]:
                    handler.close()
                    self.debug_logger.removeHandler(handler)
                self.debug_logger.info(f'战斗 {self.battle_id} 日志记录器已清理')
            except Exception as e:
                logger.error(f'Error cleaning up debug logger for battle {self.battle_id}: {e}')

        logger.info(f'Battle {self.battle_id} stopped and cleaned up')


# 全局战斗引擎管理器
class BattleEngineManager:
    """战斗引擎管理器"""

    def __init__(self):
        self.engines: dict[str, BattleEngine] = {}

    def start_battle(self, battle_id: str) -> bool:
        """启动战斗"""
        if battle_id in self.engines:
            logger.warning(f'战斗 {battle_id} 已在运行')
            return False

        engine = BattleEngine(battle_id)

        if engine.initialize():
            self.engines[battle_id] = engine

            # 在后台运行战斗，并添加异常处理
            def run_battle_with_error_handling():
                try:
                    engine.start_battle()
                except Exception as e:
                    logger.error(f'战斗执行失败 {battle_id}: {e}')
                    engine.debug_logger.error(f'战斗执行失败 {battle_id}: {e}')
                    
                    # 将战斗状态设置为error
                    try:
                        from .models import Battle
                        battle = Battle.objects.get(id=battle_id)
                        if battle.status != 'error':  # 避免重复设置
                            battle.status = 'error'
                            battle.error_message = f'战斗执行异常: {str(e)}'
                            battle.finished_at = timezone.now()
                            battle.save(update_fields=['status', 'error_message', 'finished_at'])
                            logger.info(f'战斗 {battle_id} 状态已设置为error')
                    except Exception as save_error:
                        logger.error(f'更新战斗 {battle_id} 错误状态失败: {save_error}')
                    
                    # 确保从engines中移除失败的战斗
                    if battle_id in self.engines:
                        del self.engines[battle_id]

            # 使用线程来运行战斗，避免阻塞主线程
            import threading

            battle_thread = threading.Thread(target=run_battle_with_error_handling)
            battle_thread.daemon = True
            battle_thread.start()

            return True
        else:
            # 初始化失败时，将战斗状态设置为error
            try:
                from .models import Battle
                battle = Battle.objects.get(id=battle_id)
                battle.status = 'error'
                battle.error_message = '战斗服务器启动失败'
                battle.finished_at = timezone.now()
                battle.save(update_fields=['status', 'error_message', 'finished_at'])
                logger.error(f'战斗 {battle_id} 初始化失败，状态已设置为error')
            except Exception as save_error:
                logger.error(f'更新战斗 {battle_id} 错误状态失败: {save_error}')
            return False

    def stop_battle(self, battle_id: str) -> bool:
        """停止战斗"""
        engine = self.engines.get(battle_id)
        if engine:
            engine.stop_battle()
            del self.engines[battle_id]
            return True
        return False

    def set_battle_verbose_state(self, battle_id: str, enabled: bool) -> bool:
        """设置指定战斗的详细battleState日志开关

        Args:
            battle_id: 战斗ID
            enabled: True表示开启详细日志，False表示关闭

        Returns:
            bool: 设置是否成功
        """
        engine = self.engines.get(battle_id)
        if engine:
            engine.set_verbose_battle_state(enabled)
            return True
        return False

    def get_running_battles(self) -> list[str]:
        """获取正在运行的战斗列表"""
        return list(self.engines.keys())


# 全局实例
_battle_engine_manager = None


def get_battle_engine_manager() -> BattleEngineManager:
    """获取战斗引擎管理器实例"""
    global _battle_engine_manager
    if _battle_engine_manager is None:
        _battle_engine_manager = BattleEngineManager()
    return _battle_engine_manager
